﻿using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Devices.HumanInterfaceDevice;
using Windows.Devices.SerialCommunication;
using Windows.Foundation;
using Windows.Storage;
using Windows.Storage.Streams;
using static FASTControlUI.GameDevice;

namespace FASTControlUI
{
    interface DeviceControlerInterface<DeviceType>
    {
        ushort VID { get; set; }
        ushort PID { get; set; }
        ushort Page { get; set; }
        ushort Id { get; set; }
        DeviceType device { get; set; }
        DeviceWatcher deviceWatcher { get; set; }
        FileAccessMode fileAccessMode { get; set; }
        TypedEventHandler<DeviceWatcher, DeviceInformationUpdate> RemoveDevice { get; set; }
        TypedEventHandler<DeviceWatcher, DeviceInformation> AddDevice { get; set; }
    }
    abstract internal class DeviceControler<DeviceType> : DeviceControlerInterface<DeviceType>
    {
        public ushort VID { get; set; }
        public ushort PID { get; set; }
        public ushort Page { get; set; }
        public ushort Id { get; set; }
        public bool isDeviceConnect { get; set; }
        public DeviceType device { get; set; }
        public DeviceWatcher deviceWatcher { get; set; }
        public FileAccessMode fileAccessMode { get; set; }
        public TypedEventHandler<DeviceWatcher, DeviceInformationUpdate> RemoveDevice { get; set; }
        public TypedEventHandler<DeviceWatcher, DeviceInformation> AddDevice { get; set; }
        // 函数内定义 设备插入 删除事件
        public abstract void initDeviceEvent();
        public void RegisterDeviceConnectWatcherAsync()
        {
            initDeviceEvent();
            deviceWatcher.Added += AddDevice;
            deviceWatcher.Removed += RemoveDevice;
            if ((deviceWatcher.Status != DeviceWatcherStatus.Started) && (deviceWatcher.Status != DeviceWatcherStatus.EnumerationCompleted))
            {
                deviceWatcher.Start();
            }
        }
        public T BytesToStruct<T>(byte[] bytes)
        {
            int size = Marshal.SizeOf<T>();
            IntPtr point = Marshal.AllocHGlobal(size);
            try
            {
                Marshal.Copy(bytes, 0, point, size);
                return Marshal.PtrToStructure<T>(point);
            }
            finally
            {
                Marshal.FreeHGlobal(point);
            }
        }
        public byte[] StructToBytes<T>(T t)
        {
            int size = Marshal.SizeOf<T>();
            byte[] bytes = new byte[size];
            IntPtr point = Marshal.AllocHGlobal(size);
            try
            {
                Marshal.StructureToPtr<T>(t, point, false);
                Marshal.Copy(point, bytes, 0, size);
                return bytes;
            }
            finally
            {
                Marshal.FreeHGlobal(point);
            }
        }
    }
    internal class GameDevice : DeviceControler<HidDevice>
    {
        GameReportArgs reportArgs = new GameReportArgs();
        event TypedEventHandler<GameDevice, GameReportArgs> InputReportHandle;
        Action<InputReport> action;
        private TypedEventHandler<HidDevice, HidInputReportReceivedEventArgs> inputReportEventHandler { get; set; }
        public GameDevice(ushort VID, ushort PID, Action<InputReport> inputEvent)
        {
            action = inputEvent;
            Page = 1;
            Id = 4;
            fileAccessMode = FileAccessMode.Read;
            this.VID = VID;
            this.PID = PID;
            this.inputReportEventHandler = InputReportEvent;
            RegisterDeviceConnectWatcherAsync();
        }
        public GameDevice(ushort VID, ushort PID)
        {
            Page = 0xff01;
            Id = 1;
            fileAccessMode = FileAccessMode.ReadWrite;
            this.VID = VID;
            this.PID = PID;
            RegisterDeviceConnectWatcherAsync();
        }
        public event TypedEventHandler<GameDevice, GameReportArgs> InputReportHandleEvent
        {
            add
            {
                InputReportHandle += value;
            }
            remove
            {
                InputReportHandle -= value;
            }
        }
        public class GameReportArgs : EventArgs
        {
            public InputReport Report;
        }
        public override void initDeviceEvent()
        {
            string selector = HidDevice.GetDeviceSelector(Page, Id, VID, PID);
            deviceWatcher = DeviceInformation.CreateWatcher(selector);
            RemoveDevice = new TypedEventHandler<DeviceWatcher, DeviceInformationUpdate>((watcher, infoUp) =>
            {
                if (device != null)
                {
                    UnRegisterInputReportReceived();
                    device.Dispose();
                    device = null;
                    isDeviceConnect = false;
                }
            });
            AddDevice = new TypedEventHandler<DeviceWatcher, DeviceInformation>(async (watcher, infoUp) =>
            {
                device = await HidDevice.FromIdAsync(infoUp.Id, fileAccessMode);
                isDeviceConnect = true;
                RegisterInputReportReceived();

            });
        }
        void InputReportEvent(HidDevice sender, HidInputReportReceivedEventArgs args)
        {
            HidInputReport inputReport = args.Report;
            IBuffer buffer = inputReport.Data;
            using (var dr = DataReader.FromBuffer(buffer))
            {
                byte[] bytes = new byte[inputReport.Data.Length];
                dr.ReadBytes(bytes);
                reportArgs.Report = BytesToStruct<InputReport>(bytes);
                action(reportArgs.Report);
                if (InputReportHandle != null)
                    InputReportHandle(this, reportArgs);
            }
        }
        private void RegisterInputReportReceived()
        {
            if (device != null)
                if (inputReportEventHandler != null)
                    device.InputReportReceived += inputReportEventHandler;
        }
        private void UnRegisterInputReportReceived()
        {
            if (device != null)
                if (inputReportEventHandler != null)
                    device.InputReportReceived -= inputReportEventHandler;
        }
        public async Task<uint> Write<T>(T report)
        {
            if (device == null)
                return 0;
            if (fileAccessMode == FileAccessMode.Read)
                return 0;
            using (var dataWriter = new DataWriter())
            {
                byte[] outport = StructToBytes(report);
                dataWriter.WriteBytes(outport);
                HidOutputReport outReport = device.CreateOutputReport(outport[0]);
                outReport.Data = dataWriter.DetachBuffer();
                try
                {
                    return await device.SendOutputReportAsync(outReport);
                }
                catch (COMException exption)
                {
                    Console.WriteLine(exption.Message);
                    return 0;
                }
                finally
                {
                    dataWriter.Dispose();
                }
            }
        }
       
    }

    public struct InputReport
    {
        public byte id;
        public byte hatSwitch;
        public ushort buttons;
        public ushort axisRx;
        public ushort axisRy;
        public short axisX;
        public short axisY;
        public short wheel;
        public ushort Throttle;
        public ushort Brake;
        public short Force;
    };
    public struct OutputReportWheel
    {
        public byte id;
        public byte WheelFlag;        // 0x01 量程设置 0x44 中点校准
        public byte Force;            // 力反馈强度
        public byte Friction;         // 阻尼
        public byte Gain;             // 增益
        public byte Antigravity;      // 反重力
        public short WheelEngle;               // 角度
        public short WheelEncoderCPR; // 编码器线数x4
        public short WheelDegrees;    // 最大量程
        public byte WheelStop;        // 限位力
        public byte WheelCenter;      // 回中力
        public short temp1;
        public short temp2;
        public short temp3;
    }
    public struct OutpurReportPedal
    {
        public byte id;
        public byte RxLow;
        public byte RxHigh;
        public byte RyLow;
        public byte RyHigh;
        public byte BrakeLow;
        public byte BrakeHigh;
        public byte ThrottleLow;
        public byte ThrottleHigh;
        public byte waveFiltering;
        public byte Flags;
        public byte temp;
        public byte RxBezier;
        public byte RyBezier;
        public byte BrakeBezier;
        public byte ThrottleBezier;
        public short temp3;
        public short temp4;
    }
    internal class GameSerialDevice : DeviceControler<SerialDevice>
    {
        DataReader dataReader;
        DataWriter dataWriter;
        SerialParity Parity;
        SerialStopBitCount StopBitCount;
        uint BaudRate;
        ushort DataBits;
        public GameSerialDevice(ushort VID, ushort PID, uint BaudRate = 921600, ushort DataBits = 8, SerialParity Parity = SerialParity.None, SerialStopBitCount StopBitCount = SerialStopBitCount.One)
        {
            this.VID = VID;
            this.PID = PID;
            this.BaudRate = BaudRate;
            this.DataBits = DataBits;
            this.Parity = Parity;
            this.StopBitCount = StopBitCount;
            RegisterDeviceConnectWatcherAsync();
        }
        public override void initDeviceEvent()
        {
            string selector = SerialDevice.GetDeviceSelectorFromUsbVidPid(VID, PID);
            deviceWatcher = DeviceInformation.CreateWatcher(selector);
            RemoveDevice = new TypedEventHandler<DeviceWatcher, DeviceInformationUpdate>((watcher, infoUp) =>
            {
                if (device != null)
                {
                    dataReader.Dispose();
                    dataWriter.Dispose();
                    device.Dispose();
                    device = null;
                    isDeviceConnect = false;
                }
            });
            AddDevice = new TypedEventHandler<DeviceWatcher, DeviceInformation>(async (watcher, infoUp) =>
            {
                device = await SerialDevice.FromIdAsync(infoUp.Id);
                device.BaudRate = BaudRate;
                device.DataBits = DataBits;
                device.Parity = Parity;
                device.StopBits = StopBitCount;
                device.ReadTimeout = TimeSpan.FromMilliseconds(10);
                device.WriteTimeout = TimeSpan.FromMilliseconds(10);
                dataReader = new DataReader(device.InputStream);
                dataReader.InputStreamOptions = InputStreamOptions.Partial;
                dataWriter = new DataWriter(device.OutputStream);
                isDeviceConnect = true;
                _ = Write("game 1");
            });
        }
        public async Task<uint> Write(string input)
        {
            dataWriter.WriteString(input);
            return await dataWriter.StoreAsync().AsTask();
        }
        public async Task<uint> Write<T>(T Struct) where T : SerialStruct
        {
            byte[] outport = StructToBytes(Struct);
            dataWriter.WriteBytes(outport);
            return await dataWriter.StoreAsync().AsTask();
        }
        public async Task<string> Read()
        {
            Task<UInt32> loadAsyncTask = dataReader.LoadAsync(1024).AsTask();
            UInt32 bytesRead = await loadAsyncTask;
            return dataReader.ReadString(bytesRead);
        }
        public async Task<byte[]> ReadBytes()
        {
            Task<UInt32> loadAsyncTask = dataReader.LoadAsync(1024).AsTask();
            UInt32 bytesRead = await loadAsyncTask;
            byte[] bytes = new byte[bytesRead];
            dataReader.ReadBytes(bytes);
            return bytes;
        }
        public async Task<T> Read<T>() where T : SerialStruct
        {
            byte[] bytes = await ReadBytes();
            for (int i = 0; i < bytes.Length; i++)
            {
                uint checkHeader = BitConverter.ToUInt32(bytes, i);
                if (checkHeader == 0x74202d20)
                {
                    int startIndex = i;
                    bytes = bytes[startIndex..bytes.Length];
                    if (bytes.Length < Marshal.SizeOf<T>())
                    {
                        Task<UInt32> loadAsyncTask = dataReader.LoadAsync(1024).AsTask();
                        UInt32 bytesRead = await loadAsyncTask;
                        byte[] add = new byte[bytesRead];
                    };
                    break;
                }
            }
            return BytesToStruct<T>(bytes);
        }
        event TypedEventHandler<GameSerialDevice, SerailArgs> InputMessage;
        public event TypedEventHandler<GameSerialDevice, SerailArgs> InputMessageEvent
        {
            add
            {
                InputMessage += value;
            }
            remove
            {
                InputMessage -= value;
            }
        }
        public class SerailArgs : EventArgs
        {
            public byte[] data;
        }
        Thread RecviceThread;
        bool RecviceNext = false;
        public void LoopRecviceStart()
        {
            RecviceNext = true;
            RecviceThread = new Thread(() =>
            {
                while (RecviceNext)
                {
                    InputLoop();
                }
            });
        }
        public void LoopRecviceStop()
        {
            RecviceNext = false;
        }
        private async void InputLoop()
        {
            SerailArgs data = new SerailArgs();
            data.data = await ReadBytes();
            if (InputMessage != null)
                InputMessage(this, data);
        }
        public bool DTR { get { return device.IsDataTerminalReadyEnabled; } set { device.IsDataTerminalReadyEnabled = value; } }
        public bool RTS { get { return device.IsRequestToSendEnabled; } set { device.IsRequestToSendEnabled = value; } }
        public interface SerialStruct
        {
            uint CheckHeader { get; set; }
        }
        public struct ConfigType : SerialStruct
        {
            public uint CheckHeader { get; set; }
            public uint temp;
            public uint temp1;
            public uint temp2;
            public char number1;
            public char number2;
            public char number3;
            public char number4;
        }
    }
}
